class Aio::DeviceManager

  # 保存所有设备类
  # devices = {"device_name" => device_klass}
  attr_accessor :devices

  # 按设备型号保存cmd
  # cmds = {"device_type" => [module_klass1, module_klass2]}
  attr_accessor :cmds

  # 命令的正则表达式匹配
  # cmds_reg = {module_klass => ["device_type", "cmd_reg"]}
  attr_accessor :cmds_reg

  # 警告信息汇总类
  attr_reader	:warning_summarize

  # 模块管理信息,只读
  attr_accessor :module_manager

  # 当初始化完成，所有cmd模块已经整理完毕
  def initialize(module_manager)
    self.cmds = {}
    self.cmds_reg = {}
    self.devices = {}
    @module_manager = module_manager
    @warning_summarize = Aio::Warning::WarningSummarize.new(self)
    tidy_cmds
  end

  def []=(key, val)
    @devices[key] = val
  end

  def [](key)
    @devices[key]
  end

  # 设备台数
  def devices_number
    self.devices.keys.size
  end

  def inspect
    total = devices_number
    names = []
    each_devices do |name, klass|
      names << name
    end

    "#<Aio::DeviceManager @device_number: #{total}, @devices_name=#{names.to_s} >"
  end

  # 整理cmds 和 cmds_reg
  # 返回为Hash = {"cisco" => [klass, klass]}
  def tidy_cmds
    device_type = @module_manager.get_modules_device_type_to_s

    device_type.each do |type|
      # 整理cmds
      type_modules = @module_manager.get_modules_by_device_type(type)
      self.cmds[type] = type_modules

      # 整理cmds_reg
      type_modules.each do |cmd_klass|

        # 将cmd_short转变为正则表达式
        # 相同的cmd_short会导致重复
        cmd_short_reg = Aio::Base::Toolkit::Regexp.to_reg(cmd_klass.cmd_short)
        cmds_reg[cmd_klass] = [type, cmd_short_reg]
      end
    end
    return cmds
  end

  # 仅仅返回各个命令的正则表达式的数组,在input/console中使用
  def just_cmds_reg
    res = []
    cmds_reg.each_pair do |klass, info|
      res << info[1]
    end
    return res
  end

  def each_devices
    devices.each_pair do |device_name, device_klass|
      yield device_name, device_klass
    end
  end

  def each_devices_with_index
    i = 0
    devices.each_pair do |device_name, device_klass|
      yield device_name, device_klass, i
      i += 1
    end
  end

  # 轮询设备名以及有用信息
  def each_devices_with_useful
    each_devices do |device_name, device_klass|
      useful = device_klass.cmds_useful
      yield device_name, useful
    end
  end

  # 只返回按照特定的设备,types 为数组
  def each_devices_of(*types)
    each_devices do |name, klass|
      next unless types.include? klass.device_type
      yield klass
    end
  end

  # 返回只是cisco的设备
  def each_devices_of_cisco
    each_devices do |name, klass|
      next unless klass.device_type == 'cisco'
      yield klass
    end
  end

  # 返回只是h3c的设备
  def each_devices_of_h3c
    each_devices do |name, klass|
      next unless klass.device_type == 'h3c'
      yield klass
    end
  end

  # 返回只是maipu的设备
  def each_devices_of_maipu
    each_devices do |name, klass|
      next unless klass.device_type == 'maipu'
      yield klass
    end
  end

  # 判断设备名是否存在
  def device_exist?(opts={})
    each_devices do |devices, _|
      if devices == opts[:device_name]
        return true
      end
    end
    return false
  end

  # 设备类型分类
  # 返回 { "cisco" => [device_name1, ...] }
  def device_type_classify
    res = {}
    each_devices do |device_name, device_klass|
      type = device_klass.device_type
      unless res.has_key?(type)
        res[type] = []
      end
      res[type] << device_name
    end

    return res
  end
  # 查找是否有符合指定的cmd模块
  # 有可能两种设备类型同时存在同样的命令，在不确定设备类型的情况下，全部输出
  # 注意此处输出的样式为 res = [ [device_type, cmd_klass], ... ]
  # info = { cmd_klass => [type, cmd_reg] }
  def cmd_module_assign(opts)
    cmd = opts[:cmd]
    res = []

    device_type = opts[:device_type]
    cmds_reg.each do |cmd_klass, info|
      reg = info[1]
      type = info[0]
      if reg.match(cmd)

        # 判断如果没有指定device_type,那么全部输出
        # 如果指定，则只输出指定类型
        # cmd_klass 会被覆盖, 已经修复
        if device_type == type
          res << [type, cmd_klass.division]
        elsif device_type.nil?
          res << [type, cmd_klass.division]
        end

      end
    end

    return res.empty? ? nil : res
  end


  # 在状态机中使用了，添加设备，并添加cmd-context
  def add_device(opts)

    # 判断是否已经有了device，如果有，则沿用，如果没有则新建
    if has_device?(opts)
      device_klass = self.devices[opts[:device_name]]
    else
      device_klass = Aio::Device::ParentDevice.new
      device_klass.device_name = opts[:device_name]
      device_klass.manager_ip = opts[:manager_ip] if opts[:manager_ip]
    end

    # cmd 和 context 必须同时存在的情况下才能加载的
    if opts[:cmd] and opts[:context]

      cmd_arr = cmd_module_assign(opts)
      if cmd_arr.nil?
        device_klass.add_cmd_context_by_cmd_name(
          opts[:cmd],
          opts[:context]
        )
      elsif cmd_arr.size == 1
        device_klass.add_cmd_context_by_klass(
          cmd_arr[0],
          opts[:context]
        )
      else
        # 有多个cmd模块的情况，全部依次加进去，但是最后分析的时候，出现明显问题的将被删除
        cmd_arr.each do |cmd_klass|
          device_klass.add_cmd_context_by_klass(
            cmd_klass,
            opts[:context]
          )
        end
      end

    end

    self.devices[opts[:device_name]] = device_klass
  end

  # 将compare中得到的warning信息合并到各个设备的warning_klass中
  def merge_warning(info)

    # info = {
    #    cm1: [ [device_name, cmd, str], .. ],
    #    cm2: [ [device_name, cmd, str], .. ]
    # }
    info.each_pair do |key, element|

      element.each do |e|
        name = e.shift
        # e = [cmd, match_str_info]
        self[name].warning_klass.warning_compare({cm: key, e: e})
      end
    end
  end

  # 整理汇总警告信息
  def warning_summarize_tidy
    @warning_summarize.tidy_warning
  end

  # 是否已经有device_name
  def has_device?(opts)
    self.devices.has_key?(opts[:device_name])
  end

end
